package com.jichangxiu.framework.handler;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.entity.bo.Payload;
import com.jichangxiu.common.entity.vo.Result;
import com.jichangxiu.common.utils.JcxContextUtils;
import com.jichangxiu.common.utils.StrUtils;
import com.jichangxiu.framework.entity.bo.SecurityUser;
import com.jichangxiu.common.entity.rule.InfoRule;
import com.jichangxiu.framework.factory.InfoRuleAsyncFactory;
import com.jichangxiu.framework.factory.InfoRuleServiceFactory;
import com.jichangxiu.framework.utils.AsyncUtils;
import com.jichangxiu.framework.utils.InfoRuleUtils;
import com.jichangxiu.framework.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@Slf4j
public class LogoutHandler implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        if (!isCanLogout(httpServletRequest)) {
            outputStream.write(JSONUtil.toJsonStr(Result.no("当前没有登录用户，无需登出！")).getBytes());
            outputStream.flush();
            outputStream.close();
        } else {
            InfoRule logoutInfoRule = InfoRuleUtils.getLogoutInfoRule(httpServletRequest);
            // 给前端发送退出登录成功消息
            Result<String> result = null;
            try {
                // 清空 redis 缓存
                SecurityUtils.logout(httpServletRequest);
                // 清空 Spring Security 缓存
                if (ObjectUtil.isNotNull(authentication))
                    new SecurityContextLogoutHandler().logout(httpServletRequest, httpServletResponse, authentication);
                // 如果有写入响应头也需要清空
                if (Constants.JCX_PROPERTIES.getSecurityProperties().getWriteJtiToHeader())
                    httpServletResponse.setHeader(Constants.JCX_PROPERTIES.getClientProperties().getAuthToClientKey(), "");
                result = Result.ok("退出登录成功", "退出登录成功");
            } catch (Exception ex) {
                ex.printStackTrace();
                result = Result.no(StrUtils.isContainChinese(ex.getMessage()) ? ex.getMessage() : "退出登录失败");
                logoutInfoRule.setStatus(false);
                logoutInfoRule.setException(JSONUtil.toJsonStr(ex));
            } finally {
                logoutInfoRule.setResult(JSONUtil.toJsonStr(result));
                // 记录登出日志
                AsyncUtils.getINSTANCE().execute(InfoRuleAsyncFactory.recordInfoRule(InfoRuleServiceFactory.infoRuleService(), logoutInfoRule));
                outputStream.write(JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8));
                outputStream.flush();
                outputStream.close();
            }
        }
    }

    private boolean isCanLogout(HttpServletRequest httpServletRequest) throws IOException {
        Payload<SecurityUser> payload = SecurityUtils.getPayload(httpServletRequest);
        if (ObjectUtil.isEmpty(payload)) return false;
        else {
            JcxContextUtils.fillJcxContextEntityByPayload(payload.getJti(), payload.getJwt());
            SecurityUser userInfo = payload.getUserInfo();
            if (ObjectUtil.isEmpty(userInfo)) return false;
            JcxContextUtils.fillJcxContextEntityByTenant(userInfo.getTenantId(), userInfo.getTenantName(), userInfo.getTenantSource());
            JcxContextUtils.fillJcxContextEntityByUser(userInfo.getDeptId(), userInfo.getDeptName(), userInfo.getUserId(), userInfo.getUserCode(), userInfo.getLoginName());
            JcxContextUtils.fillJcxContextEntityByAuth(userInfo.getPostRuleList(), userInfo.getRoleRuleList(), userInfo.getPowerRuleList());
        }
        return true;
    }

}
